//
// Created by lz on 2021/2/7.
//

#ifndef MYWEBLIB_BASE_LOGGING_H_
#define MYWEBLIB_BASE_LOGGING_H_

#include "base/LogStream.h"
#include "base/Timestamp.h"

namespace lzweb
{
	class TimeZone;
	class Logger
	{
	 public:
		enum LogLevel
		{
			TRACE,
			DEBUG,
			INFO,
			WARN,
			ERROR,
			FATAL,
			NUM_LOG_LEVELS
		};

		//获取源文件的名字
		class SourceFile
		{
		 public:
			template<int N>
			SourceFile(const char (& arr)[N])   //传入数组引用，防止数组退化成指针
				:data_(arr), size_(N - 1)
			{
				//参数 str 所指向的字符串中搜索最后一次出现字符 c（一个无符号字符）的位置。
				const char* slash = strrchr(data_, '/');
				if (slash)
				{
					data_ = slash + 1;
					size_ -= static_cast<int>(data_ - arr);
				}

			}
			explicit SourceFile(const char* filename)
				: data_(filename)
			{
				const char* slash = strrchr(data_, '/');
				if (slash)
				{
					data_ = slash + 1;
				}
				size_ = static_cast<int>(strlen(data_));
			}

			const char* data_;
			int size_;
		};

		Logger(SourceFile file, int line);
		Logger(SourceFile file, int line, LogLevel level);
		Logger(SourceFile file, int line, LogLevel level, const char* func);
		Logger(SourceFile file, int line, bool toAbort);
		~Logger();

		LogStream& stream(){return  impl_.stream_;}

		static LogLevel logLevel();
		static void setLogLevel(LogLevel level);

		using OutputFunc = void(*)(const char* msg, int len);
		using FlushFunc = void(*)();
		static void setOutput(OutputFunc);
		static void setFlush(FlushFunc);
		static void setTimeZone(const TimeZone& tz);

	 private:

		class Impl
		{
		 public:
			//using LogLevel = Logger::LogLevel;
			Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
			void formatTime();
			void finish();

			TimeStamp time_;
			LogStream stream_;
			LogLevel level_;
			int line_;
			SourceFile basename_;
		};

		Impl impl_;

	};

	extern Logger::LogLevel g_logLevel;

	inline Logger::LogLevel Logger::logLevel()
	{
		return g_logLevel;
	}

#define LOG_TRACE if (lzweb::Logger::logLevel() <= lzweb::Logger::TRACE) \
    lzweb::Logger(__FILE__, __LINE__,lzweb::Logger::TRACE,__func__).stream()
#define LOG_DEBUG if (lzweb::Logger::logLevel() <= lzweb::Logger::DEBUG) \
  lzweb::Logger(__FILE__, __LINE__, lzweb::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (lzweb::Logger::logLevel() <= lzweb::Logger::INFO) \
  lzweb::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN lzweb::Logger(__FILE__, __LINE__, lzweb::Logger::WARN).stream()
#define LOG_ERROR lzweb::Logger(__FILE__, __LINE__, lzweb::Logger::ERROR).stream()
#define LOG_FATAL lzweb::Logger(__FILE__, __LINE__, lzweb::Logger::FATAL).stream()
#define LOG_SYSERR lzweb::Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL lzweb::Logger(__FILE__, __LINE__, true).stream()

	const char* strerror_tl(int saveErrno);

// Taken from glog/logging.h
//
// Check that the input is non NULL.  This very useful in constructor
// initializer lists.

#define CHECK_NOTNULL(val) \
  lzweb::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))

  template<typename T>
  T* CheckNotNull(Logger::SourceFile file, int line, const char* names, T* ptr)
  {
  	if(ptr == NULL)
	{
		Logger(file,line,Logger::FATAL).stream()<<names;
	}

  	return ptr;
  }

} //namespace lzweb

#endif //MYWEBLIB_BASE_LOGGING_H_
